﻿#include "netGame.h"
#include "chessUtill.h"
#include <QTimer>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

NetGame::NetGame(QWidget* parent)
    : Board{ parent }
{
    // 局时(20分钟)
    gameTimeLimitMs = GAME_TIME_LIMIT_MS;
    // 步时(1分钟)
    stepTimeLimitMs = STEP_TIME_LIMIT_MS;
    // 匹配超时时间(5分钟)
    matchTimeLimitMs = MATCH_TIME_LIMIT_MS;
    isRed = true;
    playerId = ChessUtill::generatePlayerId();
    matchResult = MatchResult::NO_MATCH;

    retrackButton.reset(new QPushButton("悔棋", this));
    retrackButton->setGeometry(
        BOARD_LEFT_OFFSET, 
        BOARD_TOP_OFFSET + BOARD_ROW_NUM * BOARD_RECT_DIAMETER, 100, 30);
    // 连接按钮的clicked信号到成员函数
    connect(retrackButton.get(), &QPushButton::clicked, this, &NetGame::onRetrackButtonClicked);
    // 连接子界面的游戏结束信号
    connect(this, &Board::gameOver, this, &NetGame::buttonDisable);

    m_socket.reset(new QTcpSocket(this));
    connect(m_socket.get(), &QTcpSocket::readyRead, this, &NetGame::onDataReceived);
    // 监测错误信号（某些情况下 error 信号比 disconnected 更早触发）
    connect(m_socket.get(), &QAbstractSocket::errorOccurred, this, [this](QAbstractSocket::SocketError error) {
        if (error == QAbstractSocket::RemoteHostClosedError) {
            qDebug() << "服务端主动断开连接";
            handleServerDisconnected();
        }
        });
}

// 玩家设置阵营 1:红  2:黑
void NetGame::setPlayerSide(bool otherIsRed) {
    // true:上红下黑  false:上黑下红
    redTopBlackBottom = otherIsRed;
    initPieces();
    qDebug() << isRed;
    qDebug() << playerId;
}

bool NetGame::movePiece(int row, int col, int secondId) {
    // 不是本方走棋拒绝走棋
    if (redTopBlackBottom && isRedTurn) {
        return false;
    }
    int fromRow = (selectId != -1) ? pieces[selectId].row : -1;
    int fromCol = (selectId != -1) ? pieces[selectId].col : -1;
    auto step = QSharedPointer<Step>::create(selectId, fromRow,
        fromCol, row, col, secondId);
    m_socket->write(NetworkPacket::buildMoveRequest(roomId, playerId, step));

    return Board::movePiece(row, col, secondId);
}

void NetGame::handleServerDisconnected() {
    emit matchFailed("服务端主动断开连接");
}

void NetGame::startMatchmaking() {
    // 获取匹配结果
    QHostAddress serverAddr(SERVER_DOMAIN);
    if (m_socket->state() != QAbstractSocket::ConnectedState) {
        m_socket->connectToHost(serverAddr, 8888);
    }
    m_socket->write(NetworkPacket::buildMatchRequest(playerId, gameTimeLimitMs, stepTimeLimitMs));
    // 开启定时器, 超过匹配超时时间就主动断开连接
    QTimer::singleShot(matchTimeLimitMs, this, [this]() {
        if (matchResult == MatchResult::MATCH_WAITING) {
            emit matchFailed("匹配超时");
        }
        });
}

void NetGame::onDataReceived() {

    while (m_socket->bytesAvailable() >= sizeof(NetworkHeader)) {
        NetworkHeader header;
        // 读取头部
        if (!readHeaderFromSocket(header)) {
			qWarning() << "Failed to read header!";
			return;
		}

		// 读取数据
        if (header.magic != 0x88E4C5AA) {
            m_socket->readAll(); // 清空错误数据
            continue;
        }

        if (m_socket->bytesAvailable() < header.length) {
            return; // 等待完整数据
        }

        QByteArray data = m_socket->read(header.length);

        CommandType command = static_cast<CommandType>(header.command);
        processPacket(command, data);
    }
}

// 读取头部
bool NetGame::readHeaderFromSocket(NetworkHeader& header) {
    if (m_socket->state() != QAbstractSocket::ConnectedState) {
        qWarning() << "Socket is not connected!";
        return false;
    }

    // 1. 检查是否有足够的数据
    const int headerSize = sizeof(header.magic) + sizeof(header.version) + 
        sizeof(header.algorithm) + sizeof(header.command) + sizeof(header.length); // 12 字节
    if (m_socket->bytesAvailable() < headerSize) {
        qWarning() << "Not enough data available!";
        return false;
    }

    // 2. 读取数据
    QByteArray headerData = m_socket->read(headerSize);
    if (headerData.size() != headerSize) {
        qWarning() << "Failed to read full header!";
        return false;
    }

    // 3. 解析数据（使用 QDataStream）
    QDataStream stream(headerData);
    stream.setByteOrder(QDataStream::BigEndian); // 必须与写入端一致

    stream >> header.magic;
    stream >> header.version;
    stream >> header.algorithm;
    stream >> header.command;
    stream >> header.length;

    if (stream.status() != QDataStream::Ok) {
        qWarning() << "Failed to parse header!";
        return false;
    }

    return true;
}

void NetGame::processPacket(CommandType cmd, QByteArray data) {
    switch (cmd)
    {
    case CommandType::HEARTBEAT:
        qDebug() << "HEARTBEAT";
        break;
    case CommandType::ERROR:
        qDebug() << "ERROR";
        break;
    case CommandType::MATCH_REQUEST:
        qDebug() << "MATCH_REQUEST";
        break;
    case CommandType::MATCH_SUCCESS:
        qDebug() << "MATCH_SUCCESS";
        handleMatchSuccessData(data);
        break;
    case CommandType::MATCH_FAILED:
        qDebug() << "MATCH_FAILED";
        matchResult = MatchResult::MATCH_FAILED;
        break;
    case CommandType::MOVE:
        qDebug() << "MOVE";
        handleMoveData(data);
        break;
    case CommandType::UNDO_REQUEST:
        qDebug() << "UNDO_REQUEST";
        break;
    case CommandType::DRAW_OFFER:
        qDebug() << "DRAW_OFFER";
        break;
    case CommandType::GAME_START:
        qDebug() << "GAME_START";
        break;
    case CommandType::GAME_OVER:
        qDebug() << "GAME_OVER";
        break;
    case CommandType::GAME_SAVE:
        qDebug() << "GAME_SAVE";
        break;
    default:
        break;
    }
}

void NetGame::handleMoveData(QByteArray data) {
    QJsonObject jsonObj = parseJsonObject(data);
    int fromRow = -1;
    int fromCol = -1;
    int toRow = -1;
    int toCol = -1;
    if (jsonObj.contains("move")) {
        QJsonObject moveObj = jsonObj["move"].toObject();
        if (moveObj.contains("from_x") && moveObj.contains("from_y")
            && moveObj.contains("to_x") && moveObj.contains("to_y")) {
			fromRow = moveObj["from_x"].toInt();
			fromCol = moveObj["from_y"].toInt();
			toRow = moveObj["to_x"].toInt();
			toCol = moveObj["to_y"].toInt();
		}
    }
    if (jsonObj.contains("action") && jsonObj["action"] == "select") {
        qDebug() << "handleMoveData select";
        Board::movePiece(toRow, toCol, getPieceId(toRow, toCol));
    }

    if (jsonObj.contains("action") && jsonObj["action"] == "move") {
        qDebug() << "handleMoveData move";
        Board::movePiece(toRow, toCol, getPieceId(toRow, toCol));
    }
}

void NetGame::handleMatchSuccessData(QByteArray data) {
    QJsonObject jsonObj = parseJsonObject(data);
    if (!jsonObj.contains("status") || jsonObj["status"] == "waiting") {
        matchResult = MatchResult::MATCH_WAITING;
        qDebug() << "waiting...";
        return;
    }
    if (jsonObj["status"] == "matched" &&
        jsonObj.contains("room_id") &&
        jsonObj.contains("opponent")) {
        QString roomId = jsonObj["room_id"].toString();
        QJsonObject opponentObj = jsonObj["opponent"].toObject();
        if (opponentObj.contains("id")
            && opponentObj["id"].isString()
            && opponentObj["isRed"].isString()) {
            QString opponentId = opponentObj["id"].toString();
            bool isRed = (opponentObj["isRed"].toString() == "true");
            matchResult = MatchResult::MATCH_SUCCESS;
            emit matchFound(roomId, opponentId, isRed);
        }
    }
}

QJsonObject NetGame::parseJsonObject(const QByteArray& jsonData) {
    // 1. 将 QByteArray 转换为 QJsonDocument
    QJsonParseError parseError;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);

    // 2. 检查 JSON 是否解析成功
    if (parseError.error != QJsonParseError::NoError) {
        qWarning() << "JSON 解析错误:" << parseError.errorString();
        return QJsonObject();
    }

    // 3. 判断 JSON 是对象还是数组
    if (jsonDoc.isObject()) {
        return jsonDoc.object();
    }
    return QJsonObject();
}

QJsonArray NetGame::parseJsonArray(const QByteArray& jsonData) {
    // 1. 将 QByteArray 转换为 QJsonDocument
    QJsonParseError parseError;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);

    // 2. 检查 JSON 是否解析成功
    if (parseError.error != QJsonParseError::NoError) {
        qWarning() << "JSON 解析错误:" << parseError.errorString();
        return QJsonArray();
    }
    if (jsonDoc.isArray()) {
        return jsonDoc.array();
    }
    return QJsonArray();
}

void NetGame::buttonDisable() {
    // 禁用按钮
    retrackButton->setDisabled(true);
}
void NetGame::buttonEnable() {
    // 启用按钮
    retrackButton->setDisabled(false);
}

void NetGame::setRoomIdAndOpponentId(QString roomId, QString opponentId) {
    	// 设置房间号和对手ID
	this->roomId = roomId;
	this->opponentId = opponentId;
}

// 悔棋
void NetGame::onRetrackButtonClicked() {
    bool isRed = !redTopBlackBottom;
    if (isRed) {
        if (redSteps.isEmpty()) {
            return;
        }
        if (!isRedTurn) {
            executeRetrack(true);
            // 切换走棋方
            isRedTurn = !isRedTurn;
        }
        else {
            executeRetrack(false);
            executeRetrack(true);
        }
    }
    else {
        if (blackSteps.isEmpty()) {
            return;
        }
        if (isRedTurn) {
            executeRetrack(false);
            // 切换走棋方
            isRedTurn = !isRedTurn;
        }
        else {
            // 如果当前想悔棋的一方正是执棋的一方, 说明对方已经走了一步
            // 那么需要先回退对方的一步棋, 再回退本方的一步棋
            executeRetrack(false);
            executeRetrack(true);
        }
    }
    update();
}


